home *** CD-ROM | disk | FTP | other *** search
/ Chip 2005 June / CHIP 2005-06.iso / program / internet / GoogleWebAcceleratorSetup.exe / GoogleWebAcceleratorSetup.msi / googlefastnet.jar / content / gwa-browser-overlay.js < prev    next >
Encoding:
Text File  |  2005-05-03  |  15.8 KB  |  500 lines

  1. // Copyright (C) 2005 and onwards Google, Inc.
  2. // Author(s): fritz, darin
  3. //
  4. // This file provides logic for updating the GWA toolbar in response to events
  5. // generated from the GWA backend.
  6. //
  7. // For each <tabbrowser> instance, we create an instance of GIWebAccClient,
  8. // which we hook up to the <tabbrowser> so that it can receive load events
  9. // from the browser.  We in turn receive events from the GIWebAccClient via
  10. // our GIWebAccObserver implementation.
  11. //
  12. // This file also implements all of the methods invoked from our XUL overlay.
  13. //
  14. // NOTE: Everything at global scope is prefixed with GWA_ to avoid conflicts
  15. // with other extensions that may overlay into browser.xul.
  16.  
  17.  
  18. /**
  19.  * URLs corresponding to the items in the help menu
  20.  */
  21. const GWA_kHelpGeneralURL =
  22.   "http://webaccelerator.google.com/support";
  23. const GWA_kHelpPrivacyURL =
  24.   "http://webaccelerator.google.com/privacy";
  25. const GWA_kHelpContactURL =
  26.   "mailto:labs+webaccelerator@google.com";
  27. const GWA_kHelpAboutURL =
  28.   "chrome://googlewebacc/content/gwa-about.html";
  29.  
  30. /**
  31.  * Name of the preference we use to store the fact that we've run before
  32.  */
  33. const GWA_kInitializedPref = "google.webacc.ui.initialized";
  34.  
  35. /**
  36.  * Reference to the GIWebAccClient instance associated with the currently
  37.  * loaded tabbrowser instance.
  38.  */
  39. var GWA_client = null;
  40.  
  41. /**
  42.  * This is our implementation of GIWebAccObserver.
  43.  */
  44. function GWA_WebAccObserver() {
  45. }
  46.  
  47. GWA_WebAccObserver.prototype = {
  48.   _active: 0,
  49.   _tickerIndex: 0,  // The index of the current speedometer image.
  50.  
  51.   /**
  52.    * This method is called by XPConnect to determine the interfaces that we
  53.    * support.
  54.    */
  55.   QueryInterface: function(iid) {
  56.     if (!iid.equals(Components.interfaces.GIWebAccObserver) &&
  57.         !iid.equals(Components.interfaces.nsISupports))
  58.       throw Components.results.NS_ERROR_NO_INTERFACE;
  59.     return this;
  60.   },
  61.  
  62.   /**
  63.    * This method is called by the client when a new page starts loading.
  64.    */
  65.   onStart: function() {
  66.     this._active = true;
  67.  
  68.     var speedo = document.getElementById("gwa-speedo-button");
  69.     if (speedo)
  70.       speedo.removeAttribute("gwa-preloaded");
  71.   },
  72.  
  73.   /**
  74.    * This method is called by the client when a new page finishes loading.
  75.    */
  76.   onStop: function() {
  77.     this._active = false;
  78.   },
  79.  
  80.   /**
  81.    * This method is called by the client when the tick count changes.
  82.    *
  83.    * @param tickCount
  84.    *        The current tick count.
  85.    */
  86.   onUpdateTicker: function(tickCount) {
  87.     // Do not update ticker if we are in excluded mode.
  88.     var excl = document.getElementById("gwa-exclude");
  89.     if (excl && excl.checked)
  90.       return;
  91.  
  92.     const MAX_TICKER_INDEX = 10;
  93.  
  94.     if (this._active) {
  95.       var newIndex = tickCount;
  96.       if (newIndex == this._tickerIndex)
  97.         return;
  98.  
  99.       // Here, we attempt to smooth out the changes in the speedo by only
  100.       // allowing it to step by only one tick per update.
  101.       if (newIndex > this._tickerIndex) {
  102.         if (++this._tickerIndex > MAX_TICKER_INDEX)
  103.           this._tickerIndex = MAX_TICKER_INDEX;
  104.       } else {
  105.         if (--this._tickerIndex < 0)
  106.           this._tickerIndex = 0;
  107.       }
  108.     } else {
  109.       if (this._tickerIndex == 0)
  110.         return;
  111.       --this._tickerIndex;
  112.     }
  113.  
  114.     // update the selected image
  115.     var speedo = document.getElementById("gwa-speedo-button");
  116.     if (speedo)
  117.       speedo.setAttribute("gwa-speed", this._tickerIndex);
  118.   },
  119.  
  120.   /**
  121.    * This method is called by the client when the benefit value changes.
  122.    *
  123.    * @param benefit
  124.    *        The current benefit value in seconds.  If this value is negative,
  125.    *        then it implies that GWA is still calibrating the benefit to the
  126.    *        user.
  127.    */
  128.   onUpdateBenefit: function(benefit) {
  129.     const SECONDS_PER_DAY = 86400.0;
  130.     const SECONDS_PER_HOUR = 3600.0;
  131.     const SECONDS_PER_MINUTE = 60.0;
  132.  
  133.     // Present a user-friendly version of the benefit value.
  134.     // TODO(darin): use the string bundle to support localization
  135.     if (benefit == "tuning") {
  136.       benefit = "Calibrating time saved...";
  137.     } else if (benefit > SECONDS_PER_DAY) {
  138.       benefit = (benefit / SECONDS_PER_DAY).toFixed(1) + " days saved";
  139.     } else if (benefit > SECONDS_PER_HOUR) {
  140.       benefit = (benefit / SECONDS_PER_HOUR).toFixed(1) + " hours saved";
  141.     } else if (benefit > SECONDS_PER_MINUTE) {
  142.       benefit = (benefit / SECONDS_PER_MINUTE).toFixed(1) + " minutes saved";
  143.     } else if (benefit > 0) {
  144.       benefit = (benefit - 0).toFixed(1) + " seconds saved";
  145.     } else {
  146.       benefit = "0.0 seconds saved";
  147.     }
  148.  
  149.     var button = document.getElementById("gwa-status-button");
  150.     if (button)
  151.       button.setAttribute("label", benefit);
  152.   },
  153.  
  154.   onUpdateExcludedState: GWA_updateExcludedState,
  155.  
  156.   onUpdateEnabledState: GWA_updateEnabledState,
  157.  
  158.   onMouseEnterAnchor: function(element) {
  159.     var spec = element.href;
  160.     if (spec == undefined)
  161.       spec = element.getAttribute("href");
  162.  
  163.     if (GWA_getWebAccService().isPreloaded(spec)) {
  164.       var speedo = document.getElementById("gwa-speedo-button");
  165.       if (speedo)
  166.         speedo.setAttribute("gwa-preloaded", "true");
  167.     }
  168.   },
  169.  
  170.   onMouseLeaveAnchor: function(element) {
  171.     var speedo = document.getElementById("gwa-speedo-button");
  172.     if (speedo)
  173.       speedo.removeAttribute("gwa-preloaded");
  174.   }
  175. };
  176.  
  177. function GWA_ProgressListenerWrapper(listener) {
  178.   this._listener = listener;
  179. }
  180.  
  181. GWA_ProgressListenerWrapper.prototype = {
  182.   _listener: null,
  183.  
  184.   QueryInterface: function(iid) {
  185.     if (iid.equals(Components.interfaces.nsIWebProgressListener) ||
  186.         iid.equals(Components.interfaces.nsISupportsWeakReference) ||
  187.         iid.equals(Components.interfaces.nsISupports))
  188.       return this;
  189.     throw Components.results.NS_ERROR_NO_INTERFACE;
  190.   },
  191.  
  192.   onStateChange: function(web_progress, request, state_flags, status) {
  193.     this._listener.onStateChange(web_progress, request, state_flags, status);
  194.   },
  195.  
  196.   onProgressChange: function(web_progress, request, cur_self_progress,
  197.                              max_self_progress, cur_total_progress,
  198.                              max_total_progress) {
  199.     this._listener.onProgressChange(web_progress, request,
  200.                                     cur_self_progress, max_self_progress,
  201.                                     cur_total_progress, max_total_progress);
  202.   },
  203.  
  204.   onLocationChange: function(web_progress, request, location) {
  205.     this._listener.onLocationChange(web_progress, request, location);
  206.   },
  207.  
  208.   onStatusChange: function(web_progress, request, status, message) {
  209.     this._listener.onStatusChange(web_progress, request, status, message);
  210.   },
  211.  
  212.   onSecurityChange: function(web_progress, request, state) {
  213.     this._listener.onSecurityChange(web_progress, request, state);
  214.   },
  215.  
  216.   onLinkIconAvailable: function(browser, href) {}
  217. };
  218.  
  219. /**
  220.  * This is a helper function for getting the GWA service.
  221.  *
  222.  * @return The GIWebAccService service (a singleton).
  223.  */
  224. function GWA_getWebAccService() {
  225.   return Components.classes["@google.com/client/webacc-service;1"].
  226.       getService(Components.interfaces.GIWebAccService);
  227. }
  228.  
  229. /**
  230.  * This is a helper function for getting the tabbrowser instance.
  231.  *
  232.  * @return The tabbrowser instance.
  233.  */
  234. function GWA_getBrowser() {
  235.   return document.getElementById("content");
  236. }
  237.  
  238. /**
  239.  * We want to place the GWA buttons next to the throbber by default, but the
  240.  * throbber may appear in different places (depending on the platform or on
  241.  * user configuration).  So look at the two likely places it could be, and
  242.  * return the ID of the toolbar that has the throbber.
  243.  *
  244.  * TODO what if they've removed the throbber?
  245.  *
  246.  * @returns String containing the name of the toolbar containing the
  247.  *          throbber box
  248.  */
  249. function GWA_toolbarWithThrobber() {
  250.   if (
  251.       (document.getElementById("toolbar-menubar").hasAttribute("currentset") &&
  252.        document.getElementById("toolbar-menubar").getAttribute("currentset")
  253.        .indexOf("throbber-box") > -1) ||
  254.       (document.getElementById("toolbar-menubar").hasAttribute("defaultset") &&
  255.        document.getElementById("toolbar-menubar").getAttribute("defaultset")
  256.        .indexOf("throbber-box") > -1) )
  257.     return "toolbar-menubar";
  258.                                                                                                                                     
  259.   return "nav-bar";
  260. }
  261.  
  262. /**
  263.  * Adds the button to the appropriate toolbar.
  264.  *
  265.  * @param buttonId
  266.  *        String containing the id of the button we'd like to add
  267.  * @param toolbarId
  268.  *        String containing the id of the toolbar to which to add the button
  269.  * @param beforeId
  270.  *        String containing the id of the item currently in the toolbar to
  271.  *        place the button before (if it exists)
  272.  */
  273. function GWA_addButtonToCurrentSet(buttonId, toolbarId, beforeId) {
  274.   // Careful not to add the button if its already there for some reason
  275.   var tb = document.getElementById(toolbarId);
  276.   var currentSet = tb.getAttribute(tb.hasAttribute("currentset") ?
  277.                                    "currentset" :
  278.                                    "defaultset");
  279.  
  280.   var ids = currentSet.split(",");
  281.   for (var i=0; i<ids.length; i++)
  282.     if (ids[i] == buttonId)
  283.       return;
  284.  
  285.   // Now actually add it.
  286.   var i = currentSet.indexOf(beforeId);
  287.   currentSet = (i > -1) ?
  288.     (currentSet.slice(0, i) + buttonId + "," + currentSet.slice(i)) :
  289.     (buttonId);
  290.  
  291.   // IMPORTANT!  The currentSet setter actually makes the changes happen, but
  292.   // it won't persist the changes without an update to the attribute value and
  293.   // a call to document.persist.
  294.   tb.setAttribute("currentset", currentSet);
  295.   tb.currentSet = currentSet;
  296.  
  297.   document.persist(toolbarId, "currentset");
  298. }
  299.  
  300. /**
  301.  * Checks to see if this is the first time we're running, and if so adds the
  302.  * button to the appropriate toolbar.  We keep track of whether we've run
  303.  * before using a preference.
  304.  */
  305. function GWA_maybeAddButtonToToolbar() {
  306.   var prefs = Components.classes["@mozilla.org/preferences;1"].
  307.       getService(Components.interfaces.nsIPrefBranch);
  308.  
  309.   if (!prefs.prefHasUserValue(GWA_kInitializedPref)) {
  310.     var toolbar = GWA_toolbarWithThrobber();
  311.  
  312.     // Be sure not to add these elements if they already exist elsewhere in the
  313.     // document.  This may happen if the user happens to reset our magic pref.
  314.     if (!document.getElementById("gwa-status-button"))
  315.       GWA_addButtonToCurrentSet("gwa-status-button", toolbar, "throbber-box");
  316.     if (!document.getElementById("gwa-speedo-button"))
  317.       GWA_addButtonToCurrentSet("gwa-speedo-button", toolbar, "throbber-box");
  318.  
  319.     prefs.setBoolPref(GWA_kInitializedPref, true);
  320.   }
  321. }
  322.  
  323. /**
  324.  * This function is called once browser.xul has loaded and our overlay has been
  325.  * processed.  We attach this function to the "load" event of the global
  326.  * window down below.
  327.  */
  328. function GWA_init(event) {
  329.   GWA_maybeAddButtonToToolbar();
  330.  
  331.   GWA_client = Components.classes["@google.com/client/webacc-client;1"].
  332.       createInstance(Components.interfaces.GIWebAccClient);
  333.   GWA_client.observer = new GWA_WebAccObserver();
  334.  
  335.   var wrapper = new GWA_ProgressListenerWrapper(GWA_client);
  336.   GWA_getBrowser().addProgressListener(wrapper,
  337.       Components.interfaces.nsIWebProgress.NOTIFY_STATE_NETWORK |
  338.       Components.interfaces.nsIWebProgress.NOTIFY_LOCATION);
  339.  
  340.   GWA_updateEnabledState(GWA_getWebAccService().isOn());
  341. }
  342.  
  343. /**
  344.  * This function is called when browser.xul is being unloaded.
  345.  */
  346. function GWA_finish(event) {
  347.   GWA_client.observer = null;
  348.   GWA_client = null;
  349. }
  350.  
  351. /**
  352.  * This function is called by the UI to load a help topic or feature page.
  353.  *
  354.  * @param key
  355.  *        The string valued identifier for the URL to show.
  356.  */
  357. function GWA_load(key) {
  358.   var port = GWA_getWebAccService().port;
  359.  
  360.   // Because the value of port may change with every invocation, we generate
  361.   // this table on each call.  We don't care about optimizing this function's
  362.   // performance since it is not called that frequently.  Convenience wins.
  363.   var URLs = {
  364.     "help" : GWA_kHelpGeneralURL,
  365.     "privacy" : GWA_kHelpPrivacyURL,
  366.     "contact" : GWA_kHelpContactURL,
  367.     "prefs" : "http://localhost:" + port + "/preferences",
  368.     "stats" : "http://localhost:" + port + "/races"
  369.   };
  370.  
  371.   GWA_getBrowser().loadURI(URLs[key], null, null);
  372. }
  373.  
  374. /**
  375.  * This function is called to update the excluded UI state.
  376.  *
  377.  * @param excluded
  378.  *        A boolean value indicating whether or not to show the current URI
  379.  *        (shown in the location bar) as excluded.
  380.  */
  381. function GWA_updateExcludedState(excluded) {
  382.   var excl = document.getElementById("gwa-exclude");
  383.   if (excl) {
  384.     excl.checked = excluded;
  385.  
  386.     // Sometimes the XBL binding may not have been applied yet, so the setter
  387.     // may not take.  If we don't do this here then the attribute might get out
  388.     // of sync with the XBL property!  Madness I tell you! :-/
  389.     excl.setAttribute("checked", excluded);
  390.   }
  391.  
  392.   var speedo = document.getElementById("gwa-speedo-button");
  393.   if (speedo)
  394.     speedo.setAttribute("gwa-excluded", excluded);
  395. }
  396.  
  397. /**
  398.  * This function is called to update the enabled UI state.
  399.  *
  400.  * @param enabled
  401.  *        A boolean value indicating whether or not to show GWA as enabled.
  402.  */
  403. function GWA_updateEnabledState(enabled) {
  404.   var speedo = document.getElementById("gwa-speedo-button");
  405.   var button = document.getElementById("gwa-status-button");
  406.  
  407.   if (enabled) {
  408.     if (speedo)
  409.       speedo.removeAttribute("gwa-disabled");
  410.     if (button) {
  411.       button.removeAttribute("disabled");
  412.       GWA_client.refreshBenefit();
  413.     }
  414.   } else {
  415.     if (speedo)
  416.       speedo.setAttribute("gwa-disabled", "true");
  417.     if (button) {
  418.       button.setAttribute("disabled", "true");
  419.       button.setAttribute("label", "");
  420.     }
  421.   }
  422. }
  423.  
  424. /**
  425.  * This function is called by the UI to toggle exclusion of the current URI on
  426.  * or off.
  427.  */
  428. function GWA_toggleExcludeOnOff() {
  429.   var excludeMenu = document.getElementById("gwa-exclude");
  430.   GWA_updateExcludedState(!excludeMenu.checked);
  431.  
  432.   var currentURI = GWA_getBrowser().webNavigation.currentURI;
  433.   GWA_getWebAccService().excludeURI(currentURI, excludeMenu.checked);
  434. }
  435.  
  436. /**
  437.  * This function is called by the UI to toggle GWA on or off.
  438.  */
  439. function GWA_toggleOnOff() {
  440.   var service = GWA_getWebAccService();
  441.   var toggle = document.getElementById("gwa-toggle");
  442.   var bundle = document.getElementById("gwa-strings");
  443.  
  444.   var label;
  445.   if (toggle.getAttribute("gwa-mode") == "stop") {
  446.     service.shutdownWarden();
  447.     label = bundle.getString("Stopping");
  448.   } else {
  449.     service.startupWarden();
  450.     label = bundle.getString("Starting");
  451.   }
  452.  
  453.   var button = document.getElementById("gwa-status-button");
  454.   if (button) {
  455.     button.setAttribute("label", label);
  456.     button.setAttribute("disabled", "true");
  457.   }
  458. }
  459.  
  460. /**
  461.  * This function is called by the UI to update the GWA menu just before it is
  462.  * shown to the user.  We need to update the label on the on/off toggle
  463.  * <menuitem> based on the state of the warden.
  464.  */
  465. function GWA_updateMenu() {
  466.   var prefs = document.getElementById("gwa-prefs");
  467.   var stats = document.getElementById("gwa-stats");
  468.   var excl = document.getElementById("gwa-exclude");
  469.   var toggle = document.getElementById("gwa-toggle");
  470.   var bundle = document.getElementById("gwa-strings");
  471.  
  472.   var enabled = GWA_getWebAccService().isOn();
  473.   if (enabled) {
  474.     prefs.removeAttribute("disabled");
  475.     stats.removeAttribute("disabled");
  476.     excl.removeAttribute("disabled");
  477.     toggle.setAttribute("label", bundle.getString("StopText"));
  478.     toggle.setAttribute("gwa-mode", "stop");
  479.   } else {
  480.     prefs.setAttribute("disabled", "true");
  481.     stats.setAttribute("disabled", "true");
  482.     excl.setAttribute("disabled", "true");
  483.     toggle.setAttribute("label", bundle.getString("StartText"));
  484.     toggle.setAttribute("gwa-mode", "start");
  485.   }
  486.  
  487.   // Make sure that our toplevel UI is never out of sync with the menu items.
  488.   GWA_updateEnabledState(enabled);
  489. }
  490.  
  491. /**
  492.  * This function is called by the UI to show the GWA about dialog.
  493.  */
  494. function GWA_about() {
  495.   openDialog(GWA_kHelpAboutURL, "", "chrome,modal,centerscreen");
  496. }
  497.  
  498. addEventListener("load", GWA_init, false);
  499. addEventListener("unload", GWA_finish, false);
  500.